﻿using System;
using System.Collections.Generic;

namespace ProjectEuler.Linq
{
    public static class Functional
    {
        public static Func<T, TResult> Memoize<T, TResult>(this Func<T, TResult> func)
        {
            var cache = new Dictionary<T, TResult>();

            return arg => GetFromCacheOrGenerate(cache, arg, () => func(arg));
        }

        public static Func<T1, T2, TResult> Memoize<T1, T2, TResult>(this Func<T1, T2, TResult> func)
        {
            var cache = new Dictionary<Tuple<T1, T2>, TResult>();

            return (a, b) => GetFromCacheOrGenerate(cache, Tuple.Create(a, b), () => func(a, b));
        }

        private static TResult GetFromCacheOrGenerate<TKey, TResult>(Dictionary<TKey, TResult> cache, TKey key, Func<TResult> generator)
        {
            TResult result;

            if (!cache.TryGetValue(key, out result))
            {
                result = generator();
                cache.Add(key, result);
            }

            return result;
        }
    }
}
